use serde::Deserialize;
use tracing_appender::{
    non_blocking,
    non_blocking::WorkerGuard,
    rolling::{RollingFileAppender, Rotation},
};
use tracing_subscriber::{
    prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, EnvFilter,
};

#[derive(Debug, Default, Clone, Deserialize)]
pub enum FilterRotion {
    Never,
    #[default]
    Daily,
    Hourly,
    Minutely,
}

impl FilterRotion {
    pub fn rotation(&self) -> Rotation {
        match self {
            FilterRotion::Never => Rotation::NEVER,
            FilterRotion::Daily => Rotation::DAILY,
            FilterRotion::Hourly => Rotation::HOURLY,
            FilterRotion::Minutely => Rotation::MINUTELY,
        }
    }
}

fn default_root() -> String {
    "logs".to_string()
}

fn default_name() -> String {
    "logs.log".to_string()
}

#[derive(Debug, Default, Clone, Deserialize)]
pub struct LoggerConfig {
    #[serde(default = "default_root")]
    pub root: String,
    #[serde(default = "default_name")]
    pub name: String,
    pub filter: Option<String>,
    pub rotation: FilterRotion,
    #[serde(default)]
    pub console: bool,
}

impl LoggerConfig {
    /// 根据配置初始化日志系统
    pub fn init_log(&self) -> WorkerGuard {
        let filter = if let Some(filter) = &self.filter {
            tracing_subscriber::EnvFilter::try_from(filter)
                .unwrap_or_else(|_| EnvFilter::new("info"))
        } else {
            tracing_subscriber::EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| EnvFilter::new("info"))
        };
        let dir = &self.root;
        let name = &self.name;
        let file_appender = RollingFileAppender::new(self.rotation.rotation(), dir, name);

        let (non_blocking_appender, _guard) = non_blocking(file_appender);
        let file_layer = tracing_subscriber::fmt::layer()
            .with_ansi(false)
            .with_writer(non_blocking_appender);

        let sub = tracing_subscriber::registry().with(filter).with(file_layer);
        if self.console {
            sub.with(tracing_subscriber::fmt::layer()).init();
        } else {
            sub.init()
        };
        _guard
    }
}
